#!/usr/bin/env python
# encoding: utf-8
"""Process helper application"""

import subprocess,os,sys,signal,pty,time,errno,thread

efp=None

def smart_wait_for_subprocess(sp,timeout=30):
    """
    Will wait for Process given to expire, and then kill it if
    the time elapses (or never if timeout==0). 
    @param sp: Subprocess to watch
    @param timeout: timeout length in seconds
    """
    running=1
    try:
        sleeps=timeout*4
        #Just wait around for termination...
        
        while sp.poll()==None:
            #print>>efp,"WAITING:",sleeps
            sleeps-=1
            time.sleep(.25)
            os.kill(sp.pid,0)
            if sleeps<=0 and timeout>0:
                os.kill(sp.pid,signal.SIGTERM)
                break
            elif sleeps<=0:
                break
        #Did it REALLY exit? 
        try:
            os.kill(sp.pid,0)
            sleeps=20
            while sp.poll()==None:
                #print>>efp,"KILLING:",sleeps
                sleeps-=1
                time.sleep(.25)
                
                #No? KILL IT!
                if sleeps<=0 and timeout>0:
                    #print>>efp,"KILLING2:",sleeps
                    os.kill(sp.pid,signal.SIGKILL)
                    break
                elif sleeps<=0:
                    break
        except:
            running=0
    except:
        #Make REALLY effing sure it exited.
        os.kill(sp.pid,signal.SIGKILL)
        #print>>efp,"KILLING2:",sleeps
        
    
    
        
    
def call_subprocess(args,timeout=30,stdin=None):
    """
    Will take a set of args and run them as a subprocess
    call inside of a thread, which will always exit after
    timeout seconds (or never if timeout==0). Pushes stdin
    into the pipe on the communicate call if it is given.    
    Designed to be called by run_in_subprocess, as it 
    doesn't have any forking in it.
    Returns tuple of (returncode,stdout,stderr)
    @param args: Subprocess arguments
    @param timeout: max time to wait for completion
    @param stdin: Input to stuff into stdin
    """
    stdout=False
    stderr=False
    try:
        P=subprocess.Popen(args,stdout=subprocess.PIPE,\
                            stderr=subprocess.PIPE,\
                            stdin=subprocess.PIPE)
    except Exception,e:
        return (1,'',str(e))
    if stdin==None or not stdin:
        P.stdin.close()
    else:
        P.stdin.write(stdin)
        P.stdin.close()
    try:
        smart_wait_for_subprocess(P,timeout)        
    except Exception,e:
        return (1,'',str(e))
    try:
        try:
            returncode=P.returncode
            stdout=P.stdout.read()
            stderr=P.stderr.read()
            if not stderr:
                stderr=""
        except Exception,e:
            stdout="ERR:",str(e)
    finally:
        try:
            del P
        except:
            pass
    return (returncode,stdout,stderr)
        

def run_in_subprocess(args,timeout=30,stdin=None):
    """
    Will take a set of arguments and run them as a subprocess
    call, which will always exit after
    timeout seconds. Pushes stdin
    into the pipe on the communicate call if it is given.    
    Uses fork, and requires no other signals to work. Good for
    running inside of twisted.
    Returns tuple of (returncode,stdout,stderr)
    @param args: Subprocess arguments
    @param timeout: max time to wait for completion
    @param stdin: Input to stuff into stdin
    """
    returncode,stdout,stderr=(1,"",\
                        "Undefined error calling subprocess")
    pid=os.fork() 
    if pid: 
        #We are in the original process. Wait for output with waitpid...
        try:
            #print "waiting for pid:",pid
            returned=os.waitpid(pid,0)
            #print "Returned:",returned
            returncode=os.WEXITSTATUS(returned[1])
            #print "Returned from ",pid
        except Exception,e:
            returncode=1
            stderr=str(e)
        try:
            sef=open(os.path.join('/tmp',str(pid)+'.stderr'),'r')
            stderr=sef.read()
            sef.close()
            os.unlink(os.path.join('/tmp',str(pid)+'.stderr'))
        except Exception,e:
            stderr=str(e)
        try:
            sof=open(os.path.join('/tmp',str(pid)+'.stdout'),'r')
            stdout=sof.read()
            sof.close()
            os.unlink(os.path.join('/tmp',str(pid)+'.stdout'))
        except Exception,e:
            stdout=str(e)
        
    else: 
        #We are in the forked process
        pid=os.getpid()
        try:
            #os.chdir("/") 
            #os.setsid() 
            #os.umask(0)
            try:
                returncode,stdout,stderr=call_subprocess(args,timeout,stdin)
            except:
                os._exit(1)
            if returncode==None:
                os._exit(1)
            if stdout==None:
                stdout=""
            if stderr==None:
                stderr=""
            try:
                sef=open(os.path.join('/tmp',str(pid)+'.stderr'),'w')
                sef.write(stderr)
                sef.close()
            except Exception,e:
                stderr=str(e)
            try:
                sof=open(os.path.join('/tmp',str(pid)+'.stdout'),'w')
                sof.write(stdout)
                sof.close()
            except Exception,e:
                stdout=str(e)     
            os._exit(returncode)       
                
        except:
            os._exit(returncode)
        os._exit(254) #Are we REALLY sure?
    return returncode,stdout,stderr
